/*
 * Individuo.java
 *
 * Created on 21-nov-2007, 17:46:26
 *
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package ia;

import java.util.*;


/**
 *Representa a cada solucion individual con sus atributos y
 *su operador de mutacion.
 * @author Arturo Ferreira
 * @author Manuel Avalos
 */
public class Individuo {

    /** Guarda el fitness del individuo*/
    public double fitness=0;;
    /** Indica si el individuo es o no dominado*/
    //Variable utilizada en el SPEA, representa la utilidad de un individuo.
    private double streng = 0;    
    private boolean dominado;
    /** El cromosoma representa la ubicacion de una fabrica en un determinado
     * lugar. Ejemplo: cromosoma[i]=N, significa que la fabrica "N" esta en el
     * lugar "i"
     */
    private double[] cromosoma;
    /** Indica los valores correspondientes a cada objetivo*/
    //Primer objetivo, indice 0, que representa la medida del riesgo asociado con el portfolio.
    //Segundo objetivo, indice 1, que representa la medida de rendimiento del portfolio.
    private double[] vecObjetivo;
    /**Contador de nicho*/
    public double ncount;

    
    /**
     * Constructor de la clase indivuduo
     * @param cromosoma Vector utilizado que representa el cromosoma del individuo
     */
    public Individuo(double[] cromosoma) {
        this.cromosoma = cromosoma;
        this.dominado=false;
    }

    public Individuo() {
    }
    /**
     * Es el cruce que definimos para el problema Portfolio selection.
     * El cruce consiste en utilizar el cruceOXy luego le aplicamos la 
     * restriccion de que la suma de los valores de un cromosoma sea
     * igual a 1.
     * @param pareja
     * @return
     */
    public Individuo[] cruzar(Individuo pareja)
    {
        Individuo []hijos= this.cruceOX(pareja);
        double total1;
        double total2;
                
        double suma1=0;
        double suma2=0;
        for (int i = 0; i < hijos[0].cromosoma.length; i++) {
            suma1= suma1 + hijos[0].cromosoma[i];
            suma2= suma2 + hijos[1].cromosoma[i];
        }
        total1=1/suma1;
        total2=1/suma2;
        for (int i = 0; i < hijos[0].cromosoma.length; i++) {
            hijos[0].cromosoma[i]=hijos[0].cromosoma[i]*total1;
            hijos[1].cromosoma[i]=hijos[1].cromosoma[i]*total2;
        }
        return hijos;
    }
    
    /**
    *
    * Metodo utilizado para cruzar dos individuos. Realiza la tecnica de cruzamiento Order Crossover,
    * descrita en Apuntes de Coello, capitulo 6 (Pag. 152).
    * @param pareja Viene a ser el otro individuo con quien realizar el cruzamiento.
    * @return Un vector de Individuos de 2 elementos, que vienen a ser los hijos.
    *
    */
   public Individuo[] cruceOX(Individuo pareja) {
       //Vectores utilizados para guardar cromosomas
       double[] p1;
       double[] p2;
       //
       //Vector de Individos para guardar los hijos retornados
       Individuo[] hijos = new Individuo[2];
       double[] NewCromosoma;
       //Variables para sacar la subcadena de manera aleatoria
       int inicio;
       int fin;
       //La logintud de la subcadena utilizada
       int long_subcadena; 
       Random rand;
       //longitud de cromosoma
       int long_crom; 
       long_crom = this.cromosoma.length;
      
       //######Calculo para el primer hijo#########
       NewCromosoma = new double[long_crom];
       p1 = this.getCromosoma();
       p2 = pareja.getCromosoma();

       //Extraer una subcadena de manera aleatoria
       rand = new Random();
       inicio = rand.nextInt(long_crom);
       long_subcadena = rand.nextInt(long_crom - 1);
       //Poner los valores de la subcadena aleatoria en la matriz hijos
       fin = inicio;
       for (int i = 0; i < long_subcadena; i++) {
           NewCromosoma[fin] = p1[fin];
           //Sacar los valores de la subcadena de p2
           for (int x = 0; x < p2.length; x++) {
               if (p2[x] == p1[fin]) {
                   p2[x] = -1;
                   break;
               }
           }
           fin = (fin + 1) % long_crom;
       }
       //Poner los elementos de p2 que no estan estan el substring
       int indice = fin;
       for (int i = 0; i < long_crom; i++) {
           //Solo poner los elementos de p2 que NO estan en el substring que saque
           boolean insertado = false;
           if (p2[i] != -1) {
               do {
                   if ((indice < inicio || indice >= fin) || (indice >= fin && indice < inicio)) {
                       NewCromosoma[indice] = p2[i];
                       insertado = true;
                   }
                   indice = (indice + 1) % long_crom;
               } while (!insertado);
           }
       }
       hijos[0] = new Individuo(NewCromosoma);

       //######Calculo para el segundo hijo#########
       NewCromosoma = new double[long_crom];
       p2 = pareja.getCromosoma();
       //Extraer una subcadena de manera aleatoria
       rand = new Random();
       inicio = rand.nextInt(long_crom);
       long_subcadena = rand.nextInt(long_crom - 1);
       //Poner los valores de la subcadena aleatoria en la matriz hijos
       fin = inicio;
       for (int i = 0; i < long_subcadena; i++) {
           NewCromosoma[fin] = p2[fin];
           //Sacar los valores de la subcadena de p2
           for (int x = 0; x < p1.length; x++) {
               if (p1[x] == p2[fin]) {
                   p1[x] = -1;
                   break;
               }
           }
           fin = (fin + 1) % long_crom;
       }
       //Poner los elementos de p2 que no estan estan el substring
       indice = fin;
       for (int i = 0; i < long_crom; i++) {
           //Solo poner los elementos de p2 que NO estan en el substring que saque
           boolean insertado = false;
           if (p1[i] != -1) {
               do {
                   if ((indice < inicio || indice >= fin) || (indice >= fin && indice < inicio)) {
                       NewCromosoma[indice] = p1[i];
                       insertado = true;
                   }
                   indice = (indice + 1) % long_crom;
               } while (!insertado);
           }
       }
       hijos[1] = new Individuo(NewCromosoma);
       return hijos;
   }
   
      /**
   *
   * El tipo de mutacion que usamos es el uniforme, 
   * descrita en Apuntes de Coello, capitulo 8 (Pag. 170).
   * @return El individuo luego de realizar la mutacion.
   *
   */
  public void mutar() {
      //Para elejir una posicion aleatoriamente
      Random r = new Random(); 
      
      int longitud = this.cromosoma.length;
      double aux1 = 0;
      double aux2 = 0;
      //Posicion del elemento que se intercambiara.
      int index1 = r.nextInt(longitud); 
      double valorAleatorio= r.nextDouble();
      this.cromosoma[index1]=valorAleatorio;
      double suma=0.0;
      for (int i = 0; i < this.cromosoma.length; i++) {
          suma=suma+this.cromosoma[i];
      }
      for (int i = 0; i < this.cromosoma.length; i++) {
          this.cromosoma[i]=this.cromosoma[i]*1/(suma);
      }
      
  }

    /*Metodos para el acceso de los atributos*/
    public double[] getVec_objetivo() {
        return vecObjetivo.clone();
    }

    /**
    *
    * Metodo utilizado para obtener el cromosoma de un individuo.
    * @return Un clon del vector de enteros que representa al cromosoma del individuo.
    *
    */
    public double[] getCromosoma() {
        return cromosoma.clone();
    }
    /**
    *
    * Metodo utilizado para imprimir el cromosoma del individuo.
    * @return Retorna una cadena con los valores del cromosoma.
    *
    */
    public String toStringCromosoma() {
        String cadena = "";
        for (int x = 0; x < this.cromosoma.length; x++) {
            cadena = cadena+" " + Double.toString(this.cromosoma[x]);
        }
        return cadena;
    }
    /**
    *
    * Cambia el cromosoma del individuo.
    * @param cromosoma El cromosoma a ser insertado
    *
    */
    public void setCromosoma(double[] cromosoma) {
        this.cromosoma = cromosoma;
    }

    /**
    *
    * Obtiene el fitness del individuo.
    * @return El valor del fitness.
    *
    */
    public double getFitness() {
        return fitness;
    }


    /**
     *
     * Obtiene el valor guardado en una determinada posicion del cromosoma del individuo.
     * @param i Indice que indica cual alelo es seleccionado.
     * @return El valor contenido en dicha posicion.
     *
     */
    public double getAlelo (int i){
        return this.cromosoma[i];
    }
    /**
    *
    * Metodo utilizado para guardar el valor en un alelo determinado dentro del cromosoma.
    * @param i Indice que indica la posicion en el cromosoma donde ira el valor a guardar.
    * @param valor Valor a ser almacenado.
    *
    */
    public void setAlelo (int i,double valor){
        this.cromosoma[i] = valor;
    }
    /**
    *
    * Metodo utilizado para obtener el valor del individuo en un determinado objetivo.
    * @param i Indica el objetivo del cual queremos saber el valor.
    * @return El valor del objetivo.
    *
    */
    public double getValorObjetivo (int i){
        return this.vecObjetivo[i];
    }
    
    /**
    *
    * Metodo utilizado para almacenar el valor en un determinado objetivo que corresponde al individuo.
    * @param i Indica el objetivo en el cual almacenaremos el valor.
    * @param valor El valor a ser almacenado.
    *
    */
    public void setValorObjetivo (int i, int valor){
        this.vecObjetivo[i] = valor;
    }
    
    /**
    *
    * Metodo para dimensionar el vector de valores de los objetivos del individuo.
    * @param m Indica cuantos objetivos se tiene.
    *
    */
    public void dimensionarVecObjetivo(int m){
        this.vecObjetivo = new double [m];
    }
    
    /**
    *
    * Metodo para cambiar el estado del individuo.
    * @param dominado Indica si el individuo se va o no cambiar a dominado.
    *
    */
    public void setDominado(boolean dominado) {
        this.dominado = dominado;
    }
    
    /**
    *
    * Metodo que retorna si un individuo es dominado.
    * @param dominado Indica si el individuo se va o no cambiar a dominado.
    *
    */
    public boolean isDominado()
    {
        return this.dominado;
    }
    
    /**
    *
    * Metodo utilizado para saber si un individuo domina debilmente al otro, ya que en el SPEA
    * este tipo de dominancia y no la completa.
    * @param externo Individuo que se evalua para saber si es dominado por el otro individuo.
    * @return Verdadero si es dominado por el individuo, falso si no es dominado.
    *
    */
    public boolean DominaA(Individuo externo) 
    {
        double [] dominado=externo.getVec_objetivo();
        
        for (int i = 0; i < dominado.length; i++)
        {
            if (this.vecObjetivo[i] >= dominado[i] )//OJO, falta ver esta parte...
                return false;
 
        }

        return true;
    }
    /**
    *
    * Metodo utilizado para imprimir el vector de objetivos del individuo.
    * El primer objetivo es el riesgo y el segundo el rendimiento.
    * @return Una cadena que contiene los valores de cada objetivo correspondiente al individuo.
    *
    */
    public String toStringVecObjetivo() {
        String cadena = "";
        for (int x = 0; x < this.vecObjetivo.length; x++) {
            if(x==0)
                cadena = cadena+"      " + Double.toString(this.vecObjetivo[x]);
            else
            {
                double aux=(1/this.vecObjetivo[x]);
                cadena = cadena+"      " + Double.toString(aux);
            }
        }
        return cadena;
    }
    
    /**
    *
    * Metodo utilizado para hallar la distancia Euclidiana entre dos individuos.
    * @param B Es el individuo con quien se va a medir la distancia euclidiana.
    * @return La distancia euclidiana en una varible double.
    *
    */
    public double distancia(Individuo B)
    {
        double [] a=this.vecObjetivo;
        double [] b=B.vecObjetivo;
        double sumatoria=0;
        for (int i = 0; i < b.length; i++) {
            sumatoria=sumatoria+Math.pow(a[i]-b[i], 2);
        }
        return Math.sqrt(sumatoria);
   }
    /**
    * Metodo utilizado para calcular los valores del vector objetivo del individuo.
    * @param matrices Contiene la matriz de distancia y matrices de flujos utilizados para el calculo.
    *
    */ 
    public void calcularValorObjetivo(double[][] matrizVarianza, double [] vectorRendimiento){
        
        this.vecObjetivo=new double[2];
        //Calculo del primer objetivo, que representa la medida del riesgo asociado con el portfolio.
        //Se busca minimizar el riesgo.
        for (int i = 0; i < matrizVarianza.length; i++) {
            for (int j = i; j < matrizVarianza.length; j++) {
                this.vecObjetivo[0]=this.vecObjetivo[0]+ matrizVarianza[i][j]*this.cromosoma[i]*this.cromosoma[j];
            }
        }
        //Calculo del segundo objetivo, que representa la medida del rendimiento esperado en el portfolio.
        //se busca maximizar el rendimiento.
        double auxRendimiento=0.0;
        for (int i = 0; i < vectorRendimiento.length; i++) {
            auxRendimiento= auxRendimiento + vectorRendimiento[i]*this.cromosoma[i];
        }
        
        //Hacemos la inversa de la medida del rendimiento para considerar todo como una minimizacion.
        this.vecObjetivo[1]=1/(auxRendimiento);
    }
    
    /**
     * Metodo utilizado para verificar si dos individuos son iguales.
     * @param a Es el individuo con quien se compara.
     * @return True si es que son iguales.
     *
     */
     public boolean igual(Individuo a)
     {
         for (int i = 0; i < cromosoma.length; i++)
         {
             if (this.cromosoma[i]!=a.cromosoma[i]) 
             {
                return false;
             }
         }
         return true;
    }
     
     /**
     *
     * Metodo utilizado para guardar el valor del streng del individuo.
     * @param valor A ser guardado como el streng.
     *
     */
    public void setStreng(double valor) {
        //Se guarda el valor de streng del individuo.
        this.streng = valor;
    }
    
    /**
     *
     * Obtiene el streng del individuo.
     * @return El valor del streng.
     *
     */
    public double getStreng() {
        //Se retorna el streng del individuo.
        return this.streng;
    }
    
    /**
     *
     * Metodo utilizado para guardar el valor del fitness del individuo.
     * @param valor A ser guardado como el fitness.
     *
     */
    public void setFitness(double valor) {
        //Se guarda el valor del fitness del individuo.
        this.fitness = valor;
    }

    

}